<script setup>
import {computed, nextTick, ref} from "vue";
import {Button, Icon, Input, Option, Poptip, Select} from "view-ui-plus";
import {Copy} from "view-ui-plus/src";

const resValue = ref('')
const columnTypes = [{
  title: '字段名',
  key: 'fieldName'
}, {
  title: '字段类型',
  key: 'fieldType'
}, {
  title: '字段类型长度',
  key: 'fieldLen'
}, {
  title: '字段类型小数点',
  key: 'fieldDecimal'
}, {
  title: '是否为空',
  key: 'fieldNull'
}, {
  title: '注释',
  key: 'fieldDesc'
}]
const genFieldTypes = ['String', 'Integer', 'Long', 'Float', 'Double', 'BigDecimal', 'Date', 'LocalDateTime', 'Boolean', 'Short']
const tableName = ref('')
const value = ref('')
const fieldList = ref([])
const firstColumnIsField = ref(false)
const focusCol = ref('')
const fieldTypeMap = ref({
  fieldName: '',
  fieldType: '',
  fieldLen: '',
  fieldDecimal: '',
  fieldNull: '',
  fieldDesc: ''
})
const fieldGenList = ref([])
const fieldKeys = computed(() => {
  if (!fieldList.value) return []
  return fieldList.value.map((item, index) => {
    if (firstColumnIsField.value && index === 0) {
      return null
    }
    return item[0]
  }).filter(item => item !== null)
})
const editTableColumns = computed(() => {
  if (!fieldList.value) return []
  let len = 0
  for (let valueElement of fieldList.value) {
    if (valueElement.length > len) {
      len = valueElement.length
    }
  }
  const res = []
  for (let i = 0; i < len; i++) {
    const key = 'column' + (i + 1)
    let name = key
    for (let valueKey in fieldTypeMap.value) {
      if (fieldTypeMap.value[valueKey] === key) {
        const fieldType = columnTypes.find(item => item.key === valueKey)
        name = fieldType ? fieldType.title : key
      }
    }
    res.push({
      title: name,
      key: key,
      render: (h, params) => {
        const focusKey = params.index + '#' + i + '#' + key
        if (focusCol.value === focusKey) {
          const aa = h(Input, {
            modelValue: params.row[key],
            onOnChange: (e) => {
              params.row[key] = e.target.value
              fieldList.value[params.index][i] = e.target.value
            },
            onOnBlur: () => {
              focusCol.value = ''
            },
          })
          nextTick(() => {
            aa.component.ctx.focus()
          })
          return aa
        } else {
          return h('div', {
            onClick: () => {
              focusCol.value = focusKey
            }
          }, params.row[key])
        }

      },
      renderHeader: (h, params) => {
        return [params.column.title, h(Poptip, {
          transfer: true
        }, {
          default: () => h(Icon, {
            type: 'md-settings'
          }),
          content: () => h('div', {
            class: 'ivu-table-filter-list'
          }, h('ul', {
            class: 'ivu-table-filter-list-single'
          }, columnTypes.map(item => {
            const isDisabled = fieldTypeMap.value[item.key] && fieldTypeMap.value[item.key] !== params.column.key
            let className = 'ivu-table-filter-select-item' + ' ' + (fieldTypeMap.value[item.key] === params.column.key ? 'ivu-table-filter-select-item-selected' : '')
            if (isDisabled) {
              className += (' ' + 'ivu-table-filter-select-item-disabled')
            }
            return h('li', {
              class: className,
              onClick: () => {
                if (isDisabled) return
                for (let valueKey in fieldTypeMap.value) {
                  if (fieldTypeMap.value[valueKey] === params.column.key) {
                    fieldTypeMap.value[valueKey] = ''
                  }
                }
                fieldTypeMap.value[item.key] = params.column.key
                for (let valueElement of fieldGenList.value) {
                  if (item.key === 'fieldType') {
                    valueElement.genType = getJavaTypeBySqlType(valueElement[params.column.key])
                  }
                  valueElement[item.key] = valueElement[params.column.key]
                }
              }
            }, item.title)
          })))
        })]
      }
    })
  }
  res.push({
    title: '生成类型',
    key: 'genType',
    render: (h, params) => {
      const fieldGenListItem = fieldGenList.value[params.index]
      return h(Select, {
        transfer: true,
        modelValue: fieldGenListItem.genType,
        onOnChange: (val) => {
          fieldGenListItem.genType = val
        }
      }, {
        default: () => genFieldTypes.map(item => {
          return h(Option, {
            value: item
          }, {
            default: () => item
          })
        })
      })
    }
  })
  return res
})
const editTableData = computed(() => {
  if (!fieldList.value) return []
  const fieldGenListVal = []
  const resList = fieldList.value.map((item, index) => {
    if (!item || item.length <= 0) return null;
    if (item.every(item => item === null || item === '')) {
      return null;
    }
    const res = fieldGenList.value[index] || {
      genType: '',
      fieldName: '',
      fieldType: '',
      fieldLen: '',
      fieldDecimal: '',
      fieldNull: '',
      fieldDesc: ''
    }
    for (let i = 0; i < item.length; i++) {
      res['column' + (i + 1)] = item[i]
    }
    fieldGenListVal.push(Object.assign({}, res))
    return res
  }).filter(item => item !== null)
  fieldGenList.value = fieldGenListVal
  return resList
})

function getJavaTypeBySqlType(sqlType) {
  if (['varchar', 'char', 'longtext', 'mediumtext', 'text'].includes(sqlType)) {
    return genFieldTypes[0]
  } else if (['bigint', 'int8'].includes(sqlType)) {
    return genFieldTypes[2]
  } else if (['timestamp', 'date', 'datetime'].includes(sqlType)) {
    return genFieldTypes[6]
  } else if (['int', 'integer', 'smallint', 'int8', 'int4', 'int2'].includes(sqlType)) {
    return genFieldTypes[1]
  } else if (['decimal', 'double', 'float']) {
    return genFieldTypes[4]
  }
  return undefined
}

function getTypeScriptTypeByGenType(genType) {
  if (['String', 'Short'].includes(genType)) {
    return 'string'
  } else if (['Integer', 'Long', 'Float', 'Double', 'BigDecimal'].includes(genType)) {
    return 'number'
  } else if (['Date', 'LocalDateTime'].includes(genType)) {
    return 'Date'
  } else if (['Boolean'].includes(genType)) {
    return 'boolean'
  }
  return undefined
}

function getJavaDefaultByGenType(genType) {
  if (['String', 'Short'].includes(genType)) {
    return '""'
  } else if (['Integer', 'Long', 'Float', 'Double', 'BigDecimal'].includes(genType)) {
    return "null"
  } else if (['Date', 'LocalDateTime'].includes(genType)) {
    return 'new Date()'
  } else if (['Boolean'].includes(genType)) {
    return 'false'
  }
  return "null"
}

function getJsDefaultByGenType1(genType) {
  if (['String', 'Short'].includes(genType)) {
    return "''"
  } else if (['Integer', 'Long', 'Float', 'Double', 'BigDecimal'].includes(genType)) {
    return "null"
  } else if (['Date', 'LocalDateTime'].includes(genType)) {
    return 'new Date()'
  } else if (['Boolean'].includes(genType)) {
    return 'false'
  }
  return "undefined"
}

function getJsDefaultByGenType2(genType) {
  return "undefined"
}

function handleValueChange() {
  const cols = value.value.trim().split("\n")
  fieldList.value = cols.map(col => col.split("\t"))
}

function handleGen(type) {
  if (type === 'resultMap') {
    const list = ['<resultMap id="BaseResultMap" type="">']
    list.push(...fieldGenList.value.map((item, index) => {
      return `\t<result column="${item.fieldName}" property="${toHump(item.fieldName)}"/>`
    }))
    list.push('</resultMap>')
    resValue.value = list.join('\n')
  } else if (type === 'columns') {
    resValue.value = fieldGenList.value.map(item => toHump(item.fieldName)).join(', ')
  } else if (type === 'select') {
    const list = ['<select id="selectList" resultMap="">']
    const columns = fieldGenList.value.map((item, index) => {
      return `${index === 0 ? '' : '\t       '}${item.fieldName}`
    }).join(',\n')
    list.push('\tSELECT ' + columns)
    list.push(`\tfrom ${tableName.value}`)
    const wheres = fieldGenList.value.map((item, index) => {
      return `\t\t<if test="${getIfTest(item)}">
            and ${item.fieldName} = #{${toHump(item.fieldName)}}
        </if>`
    }).join('\n')
    list.push('\t<where>')
    list.push(wheres)
    list.push('\t</where>')
    list.push('</select>')
    resValue.value = list.join('\n')
  } else if (type === 'insert') {
    const list = [`<insert id="insert">\n\tinsert into ${tableName.value}`]
    list.push(`\t<trim prefix="(" suffix=")" suffixOverrides=",">`)
    list.push(...fieldGenList.value.map((item, index) => {
      return `\t\t<if test="${getIfTest(item)}">${item.fieldName},</if>`
    }))
    list.push('\t</trim>')
    list.push(`\t<trim prefix="values (" suffix=")" suffixOverrides=",">`)
    list.push(...fieldGenList.value.map((item, index) => {
      return `\t\t<if test="${getIfTest(item)}">#{${toHump(item.fieldName)}},</if>`
    }))
    list.push('\t</trim>')
    list.push('</insert>')
    resValue.value = list.join('\n')
  } else if (type === 'update') {
    const list = [`<update id="updateById">\n\tupdate ${tableName.value}`]
    list.push(`\t<trim prefix="SET" suffixOverrides=",">`)
    list.push(...fieldGenList.value.map((item, index) => {
      return `\t\t<if test="${getIfTest(item)}">${item.fieldName} = #{${toHump(item.fieldName)}},</if>`
    }))
    list.push('\t</trim >')
    list.push(`\twhere id = #{id}`)
    list.push('</update>')
    resValue.value = list.join('\n')
  } else if (type === 'delete') {
    resValue.value = `<delete id="deleteById">\n\tdelete\n\tfrom ${tableName.value}\n\twhere id = #{id}\n</delete>`
  } else if (type === 'deleteBatch') {
    const list = [`<delete id="deleteByIds">\n\tdelete from ${tableName.value} where id in`]
    list.push(`\t<foreach item="id" collection="ids" open="(" separator="," close=")">`)
    list.push('\t\t#{id}')
    list.push('\t</foreach>')
    list.push('</delete>')
    resValue.value = list.join('\n')
  } else if (type === 'entity') {
    const list = [`public class ${toHump(tableName.value)} {`]
    list.push(...fieldGenList.value.map((item, index) => {
      const note = item.fieldDesc ? `\t/**\n\t * ${item.fieldDesc}\n\t */` : ''
      return note+`\n\tprivate ${item.genType} ${toHump(item.fieldName)};`
    }))
    list.push(`}`)
    resValue.value = list.join('\n\n')
  } else if (type === 'typeScriptInterface') {
    const list = [`interface ${toHump(tableName.value)} {`]
    list.push(...fieldGenList.value.map((item, index) => {
      const note = item.fieldDesc ? `  /**\n   * ${item.fieldDesc}\n   */\n` : ''
      return note + `  ${item.fieldName}: ${getTypeScriptTypeByGenType(item.genType)}`
    }))
    list.push(`}`)
    resValue.value = list.join('\n')
  } else if (type === "defaultJavaJson") {
    const list = ['{']
    list.push(...fieldGenList.value.map((item, index) => {
      return `\t${item.fieldName}: ${getJavaDefaultByGenType(item.genType)}`
    }))
    list.push('}')
    resValue.value = list.join('\n')
  } else if (type === "defaultJsJson1") {
    const list = ['{']
    list.push(...fieldGenList.value.map((item, index) => {
      return `\t${toHump(item.fieldName)}: ${getJsDefaultByGenType1(item.genType)}`
    }))
    list.push('}')
    resValue.value = list.join('\n')
  } else if (type === "defaultJsJson2") {
    const list = ['{']
    list.push(...fieldGenList.value.map((item, index) => {
      return `\t${toHump(item.fieldName)}: ${getJsDefaultByGenType2(item.genType)}`
    }))
    list.push('}')
    resValue.value = list.join('\n')
  } else if (type === "elementTable") {

  } else if (type === "elementForm") {

  } else if (type === "elementRules") {

  }
}

function getIfTest(item) {
  const fieldNameHump = toHump(item.fieldName)
  if (item.genType === 'String') {
    return `${fieldNameHump} != null and ${fieldNameHump} != ''`
  } else {
    return `${fieldNameHump} != null`
  }
}

/**
 * 转驼峰
 * @param str 字符串
 * @param spacers 连接符 默认['-', '_']
 */
function toHump(str, ...spacers) {
  if (!spacers || spacers.length === 0) {
    spacers = ['-', '_'];
  }
  return str.replace(new RegExp('[' + spacers.join("|") + '](\\w)', 'g'), (_, c) => c ? c.toUpperCase() : '');
}

function handleCopy() {
  Copy({
    text: resValue.value
  })
}
</script>

<template>
  <div class="tool-container">
    <Alert>将 Navicat 或 DBeaver 或其他数据库工具中的字段信息复制到属性文本框中，指定字段名和字段类型即可（会自动补全生成类型）</Alert>
    <div>
      操作：
      <div>
        <Button @click="handleGen('resultMap')">resultMap</Button>
        <Button @click="handleGen('columns')">columns</Button>
        <Button @click="handleGen('select')">select</Button>
        <Button @click="handleGen('insert')">insert</Button>
        <Button @click="handleGen('update')">update</Button>
        <Button @click="handleGen('delete')">delete</Button>
        <Button @click="handleGen('deleteBatch')">deleteBatch</Button>
        <Button @click="handleGen('entity')">Entity</Button>
        <Button @click="handleGen('typeScriptInterface')">TypeScriptInterface</Button>
<!--        <Button @click="handleGen('defaultJavaJson')">DefaultJavaJson</Button>-->
        <Button @click="handleGen('defaultJsJson1')">DefaultJsJson1</Button>
        <Button @click="handleGen('defaultJsJson2')">DefaultJsJson2</Button>
        <Button @click="handleCopy">复制结果</Button>
      </div>
    </div>
    表名：<Input v-model="tableName"></Input>
    字段信息：
    <Input type="textarea" :rows="5" v-model="value" @onKeyup="handleValueChange"></Input>
    <div>
      结果：
      <Input type="textarea" :rows="10" v-model="resValue"></Input>
    </div>
    <div v-if="fieldList && fieldList.length > 0" style="max-height: 300px; overflow-y: auto">
      属性：
      <Table border :columns="editTableColumns" :data="editTableData"></Table>
    </div>
  </div>
</template>

<style scoped lang="scss">
:global(.ivu-poptip-body) {
  padding: 0 !important;
}
</style>
<style>
.ivu-table-filter-select-item-disabled {
  color: #c5c8ce;
}
</style>
